/**
  ******************************************************************************
  * @file           
  * @author         古么宁
  * @brief          电度相关
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */
/* Includes ---------------------------------------------------*/
#include <string.h>

#include "iec10x.h"
#include "iec10x_link.h"
#include "iec10x_event.h"
#include "iec10x_allpower.h"
#include "iec10x_debug.h"

#include "../iec/devif.h"

#include "COT.h"
#include "QCC.h"
#include "ASDUTYP.h"

/* Private types ------------------------------------------------------------*/
/* Private macro ------------------------------------------------------------*/
/* Private variables --------------------------------------------------------*/
/* Private function prototypes ----------------------------------------------*/

static void allpower_confirm(iec10x_t * bus);
static void allpower_dataready(iec10x_t * bus);
static void allpower_datacall(iec10x_t * bus );

/* Gorgeous Split line ------------------------------------------------------*/

/**
  * @author   古么宁
  * @brief    累计电度确认
  * @param    bus      : 总线
  * @return   don't care
*/
static void allpower_confirm(iec10x_t * bus)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t * ASDUtx = ASDU_TXBUFF(bus);
	uint8_t linkctrl ;

	ASDUtx[IEC_TYPE]          = C_CI_NA_1               ;
	ASDUtx[IEC_QUALIFIER]     = 0x01                    ;
	ASDUtx[IEC_COT]           = bus->operate.argv[1]    ;
	ASDUtx[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDUtx[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDUtx[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;

	uint8_t * info = &ASDUtx[IEC_INFO]  ;
	for (uint32_t i = 0 ; i < bus->infoaddrlen ; ++i) //填充信息对象地址，0
		*info++ = 0;
	
	*info++  = bus->operate.argv[0] ; // 在 allpower_respond() 中纪录
	
	if (bus->mode & IEC_101) {
		if (bus->mode & IEC_BALANCE) {
			int FCB = (bus->iec101.FCB == 0) ;
			bus->iec101.FCB = FCB;
			linkctrl  = IEC_CTRL(0,1,FCB,1,MASTER_TRANS);
			linkctrl |= (bus->mode & IEC_DIRECT) ;
		}
		else {
			linkctrl = IEC_CTRL(0,0,0,0,SLAVE_OK);
		}
		iec101_protocol_send(bus,ASDUtx,info - ASDUtx,linkctrl);
	}
	else {
		iec104_protocol_send(bus,ASDUtx,info - ASDUtx,0);
	}
	
	bus->operate.todo = NULL;
}

/**
  * @author   古么宁
  * @brief    累计电度数据确认帧
  * @param    bus      : 总线
  * @return   don't care
*/
static void allpower_dataready(iec10x_t * bus)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t linkctrl ;
	uint8_t * ASDUtx = ASDU_TXBUFF(bus);

	ASDUtx[IEC_TYPE]          = C_CI_NA_1               ;
	ASDUtx[IEC_QUALIFIER]     = 0x01                    ;
	ASDUtx[IEC_COT]           = COT_ACTIVATE_CONFIRM    ;
	ASDUtx[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDUtx[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDUtx[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;

	uint8_t * info = &ASDUtx[IEC_INFO]  ;
	for (uint32_t i = 0 ; i < bus->infoaddrlen ; ++i) //填充信息对象地址 0
		*info++ = 0;
	
	*info++  = bus->operate.argv[0] ; //在 allpower_respond() 中纪录

	if (bus->mode & IEC_101) {
		if (bus->mode & IEC_BALANCE) {
			int FCB = (bus->iec101.FCB == 0) ;
			bus->iec101.FCB = FCB;
			linkctrl  = IEC_CTRL(0,1,FCB,1,MASTER_TRANS);
			linkctrl |= (bus->mode & IEC_DIRECT) ;
		}
		else {
			linkctrl = IEC_CTRL(0,0,0,0,SLAVE_OK);
		}
		iec101_protocol_send(bus,ASDUtx,info - ASDUtx,linkctrl);
	}
	else {
		iec104_protocol_send(bus,ASDUtx,info - ASDUtx,0);
	}
	bus->operate.todo = allpower_datacall;
}


/**
  * @author   古么宁
  * @brief    累计电度数据帧
  * @param    bus      : 总线
  * @return   don't care
*/
static void allpower_datacall(iec10x_t * bus)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t   linkctrl ;
	uint8_t * ASDUtx = ASDU_TXBUFF(bus);

	struct iec_devif * dev ;

	ASDUtx[IEC_TYPE]          = C_CI_NA_1               ;
	ASDUtx[IEC_QUALIFIER]     = 0x01                    ;
	ASDUtx[IEC_COT]           = COT_ACTIVATE_CONFIRM    ;
	ASDUtx[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDUtx[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDUtx[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;
	ASDUtx[IEC_INFO]          = 0 ; //信息对象地址长度不定，此处 3 字节全部清零，后期覆盖
	ASDUtx[IEC_INFO+1]        = 0 ; //信息对象地址长度不定，此处 3 字节全部清零，后期覆盖
	ASDUtx[IEC_INFO+2]        = 0 ; //信息对象地址长度不定，此处 3 字节全部清零，后期覆盖

	dev = iec_devif_find(&bus->device,M_IT_NA_1);     //查找对时相关设备接口

	if (NULL == dev || NULL == dev->readfn || NULL == dev->statefn) {// 当前数据类型无可用数据接口，返回
		iec101_protocol_send(bus,NULL,0,SLVCTRL(SLAVE_NOACK,0,1));
		bus->operate.todo    = allpower_confirm;
		bus->operate.argv[1] = COT_ACTIVATE_EXIT; // 下一次召唤数据回复传输结束
		return ;
	}

	uint32_t * addrlist = NULL;
	int   infonum  = 0;
	
	dev->statefn(bus , &addrlist , &infonum); //获取设备信息，信息地址列表和信息总个数

	int maxreadnum = ((255 - 2) - IEC_INFO ) / (dev->argsize + bus->infoaddrlen) ; // 理论上一包最大能传输的信息体结构个数
	int remind = infonum - bus->operate.argv[2] ;               // 需要但剩余未传输的信息体个数
	int readnum = maxreadnum > remind ? remind : maxreadnum;   // 实际当前包能传输结构体的个数
	uint8_t * info = &ASDUtx[IEC_INFO];

	for (int i = 0 ; i < readnum ; i += 1 , info += dev->argsize) { //读取整包
		uint32_t infoaddr = addrlist[bus->operate.argv[2]++] ; //获取信息对象地址列表	
		for (int j = 0 ; j < bus->infoaddrlen ; ++j) {    //填充信息对象地址
			*info++ = (uint8_t)((infoaddr >> (j<<3)) & 0x00ff);
		}
		dev->readfn(bus,infoaddr,info); // 把信息对象地址为 infoaddr 的内容读到 info 内存中
	}

	if (bus->operate.argv[2] == infonum) { //传输完所有电度值
		bus->operate.argv[2] = 0 ;
		bus->operate.argv[1] = COT_ACTIVATE_EXIT; // 下一次召唤数据回复传输结束
		bus->operate.todo = allpower_confirm;
	}

	if (bus->mode & IEC_101) {
		if (bus->mode & IEC_BALANCE) {
			int FCB = (bus->iec101.FCB == 0) ;
			bus->iec101.FCB = FCB;
			linkctrl  = IEC_CTRL(0,1,FCB,1,MASTER_TRANS);
			linkctrl |= (bus->mode & IEC_DIRECT) ;
		}
		else {
			linkctrl = IEC_CTRL(0,0,0,0,SLAVE_OK);
		}
		iec101_protocol_send(bus,ASDUtx,info - ASDUtx,linkctrl);
	}
	else {
		iec104_protocol_send(bus,ASDUtx,info - ASDUtx,0);
	}
}





/* 响应 
 *召唤全电度 , 召唤电度分 2 步,先冻结后召唤
 * 
 */

// 一问一答过程：
// allpower_respond       冻结电量 <- 召唤一级数据 
// -> allpower_confirm    回复确认 <- 召唤电度
// -> allpower_respond    召唤电量 <- 召唤一级数据 
// -> allpower_dataready  回复确认 <- 召唤一级数据 
// -> allpower_datacall   数据发送 <- 召唤一级数据 
// -> allpower_finish     召唤结束
void allpower_respond(iec10x_t * bus , uint8_t * ASDU,int ASDUlen)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t linkctrl = 0;

	QCC_t QCC = {.val = ASDU[IEC_INFO + bus->infoaddrlen] };//跳过信息体地址为 QCC

	if (!(bus->mode & IEC_MASTER)) {
		if (bus->mode & IEC_101) {
			if (bus->mode & IEC_BALANCE){ 
				linkctrl =  IEC_CTRL(0,0,0,0,SLAVE_OK) ;
				linkctrl |= (bus->mode & IEC_DIRECT)   ;
			}
			else {
				linkctrl = IEC_CTRL(0,0,1,0,SLAVE_OK) ;
			}

			// 回复，请求 1 级数据
			iec101_protocol_send(bus,NULL,0,linkctrl);
		}
		
		bus->operate.argv[0] = QCC.val;
		bus->operate.argv[1] = COT_ACTIVATE_CONFIRM ;
		bus->operate.argv[2] = 0;

		switch (QCC.part.FRZ) {
			case FRZ_REQ_VAL:
				bus->operate.todo = allpower_dataready;
			break;

			case FRZ_NORST : // 冻结不带复位
				bus->operate.todo = allpower_confirm;
			break;

			default:
			break;
		}
	}
	else { // 主机接收到从机的反馈包
		switch (ASDU[IEC_COT]) {
		case COT_ACTIVATE_CONFIRM :
			if (FRZ_NORST == QCC.part.FRZ) {
				iec10x_msg(bus,"call QCC.part.FRZ = FRZ_REQ_VAL\r\n");
				QCC.part.FRZ                   = FRZ_REQ_VAL;
				ASDU[IEC_COT]                  = COT_ACTIVATE;
				ASDU[IEC_INFO+bus->infoaddrlen] = QCC.val;
				iec101_protocol_send(bus,ASDU,ASDUlen,MSTCTRL(MASTER_TRANS,1,0));
			}
			else {
				iec10x_msg(bus,"data ready,call it\r\n");
			}
			break;

		case COT_ACTIVATE_EXIT :
			iec10x_msg(bus,"done\r\n");
			break;
		}
	}
}



// --------------  主机操作  --------------


/**
  * @brief    主机总电度，召唤电度分 2 步,先冻结后召唤
  * @author   古么宁
  * @param    bus      : 总线
  * @param    QCCRTQ   : 电度累积量组，参考 union QCC 的 RQT 部分
  * @return   链路没问题返回 0.否则返回 1
*/
void iec101mst_allpower_req(iec10x_t * bus , uint8_t QCCRTQ)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t ASDUtx[16]; //ASDU 信息
	int ASDUlen = IEC_INFO+bus->infoaddrlen + 1;  // ASDU 总长
	QCC_t QCC = {0};
	QCC.part.RQT = QCCRTQ;
	QCC.part.FRZ = FRZ_NORST;

	ASDUtx[IEC_TYPE]          = C_CI_NA_1               ;
	ASDUtx[IEC_QUALIFIER]     = 0x01                    ;
	ASDUtx[IEC_COT]           = COT_ACTIVATE            ;
	ASDUtx[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDUtx[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDUtx[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;
	ASDUtx[IEC_INFO]          = 0 ;
	ASDUtx[IEC_INFO+1]        = 0 ;
	ASDUtx[IEC_INFO+2]        = 0 ;
	ASDUtx[IEC_INFO+bus->infoaddrlen] = QCC.val ;

	iec101_protocol_send(bus,ASDUtx,ASDUlen,MSTCTRL(MASTER_TRANS,1,0));
}


/**
  * @author   古么宁
  * @brief    主机发送召唤累积量命令
  * @param    bus      : 总线
  * @param    QCCRTQ   : 电度累积量组，参考 union QCC 的 RQT 部分
  * @return   

void iec101mst_allpower_req(iec10x_t * bus , uint8_t QCCRQT)
{
	iec101mst_event(_iec101mst_allpower_req , bus , QCCRQT);
}
*/


